package com.google.code.jtasks.manager;

import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.google.code.jtasks.connection.TaskRepoConnectionSettings;
import com.google.code.jtasks.connection.TaskRepoId;
import com.google.code.jtasks.manager.cache.IQueryCache;
import com.google.code.jtasks.manager.cache.TaskEnumsRegistry;
import com.google.inject.Inject;

/**
 * The default {@link ITaskRepoManagerFactory} implementation.
 */
public final class TaskRepoManagerFactoryImpl implements ITaskRepoManagerFactory
{
	private final Map<TaskRepoId, TaskRepoSharedResources> repoResourceMap;
	private final IQueryCache cache;

	/**
	 * Constructs a new {@link TaskRepoManagerFactoryImpl} instance.
	 */
	@Inject
	public TaskRepoManagerFactoryImpl(IQueryCache cache)
	{
		this.cache = cache;
		this.repoResourceMap = new ConcurrentHashMap<TaskRepoId, TaskRepoSharedResources>();
	}

	/**
	 * @see com.google.code.jtasks.manager.ITaskRepoManagerFactory#addConnection(com.google.code.jtasks.connection.TaskRepoConnectionSettings)
	 */
	@Override
	public void addConnection(TaskRepoConnectionSettings connection)
	{
		if (repoResourceMap.containsKey(connection.getTaskRepoId()))
			throw new IllegalArgumentException("Connection already registered for task repository: "
					+ connection.getTaskRepoId());

		TaskRepoSharedResources repoResources = new TaskRepoSharedResources(connection,
				new TaskEnumsRegistry());
		repoResourceMap.put(connection.getTaskRepoId(), repoResources);
	}

	/**
	 * @see com.google.code.jtasks.manager.ITaskRepoManagerFactory#getConnections()
	 */
	@Override
	public Set<TaskRepoConnectionSettings> getConnections()
	{
		Set<TaskRepoConnectionSettings> connections = new HashSet<TaskRepoConnectionSettings>();
		for (TaskRepoSharedResources repoResources : repoResourceMap.values())
			connections.add(repoResources.getConnection());

		return Collections.unmodifiableSet(connections);
	}

	/**
	 * @see com.google.code.jtasks.manager.ITaskRepoManagerFactory#createRepoManager(com.google.code.jtasks.connection.TaskRepoId)
	 */
	@Override
	public ITaskRepoManager createRepoManager(TaskRepoId repoId)
	{
		TaskRepoSharedResources repoResources = repoResourceMap.get(repoId);
		if (repoResources == null)
			throw new IllegalArgumentException("Unregistered task repository: " + repoId);

		return new TaskRepoManagerImpl(repoResources.getConnection(), repoResources.getRegistry(), cache);
	}

	/**
	 * Stores references to the resources shared by all {@link TaskRepoManagerImpl} instances for each task
	 * repository, i.e. there is one {@link TaskRepoSharedResources} per {@link TaskRepoId}, not per
	 * {@link TaskRepoManagerImpl} instance.
	 */
	static final class TaskRepoSharedResources
	{
		/*
		 * All shared resources must be thread-safe.
		 */

		private final TaskRepoConnectionSettings connection;
		private final TaskEnumsRegistry registry;

		/**
		 * Constructs a new {@link TaskRepoSharedResources} instance.
		 * 
		 * @param connection
		 *            the {@link TaskRepoConnectionSettings} for the task repository
		 * @param registry
		 *            the {@link TaskEnumsRegistry} for the task repository
		 */
		public TaskRepoSharedResources(TaskRepoConnectionSettings connection, TaskEnumsRegistry registry)
		{
			this.connection = connection;
			this.registry = registry;
		}

		/**
		 * Returns the {@link TaskRepoConnectionSettings} for the task repository.
		 * 
		 * @return the {@link TaskRepoConnectionSettings} for the task repository
		 */
		public TaskRepoConnectionSettings getConnection()
		{
			return connection;
		}

		/**
		 * Returns the {@link TaskEnumsRegistry} for the task repository.
		 * 
		 * @return the {@link TaskEnumsRegistry} for the task repository
		 */
		public TaskEnumsRegistry getRegistry()
		{
			return registry;
		}
	}
}
